package opencv;

import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
import org.opencv.core.*;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
import org.opencv.videoio.VideoCapture;
import org.opencv.videoio.Videoio;

/**
 * https://www.wandouip.com/t5i75082/
 * 
 * 摄像头识别人脸并自动截图
 * 以下代码与功能实现是基于JDK8 64位与OpenCV 3.2版本。
 * 运行需要配置 VM options：-Djava.library.path=C:\\opencv\\build\\java\\x64(opencv_java320.dll的位置)
 */
public class FaceCapture extends JPanel {

    private static final long serialVersionUID = 1L;
    //每一帧视频的BufferedImage对象
    private static BufferedImage mImg;
    //图片名字格式
    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyymmddHHmmss");
    //图片保存地址
    private static File path = new File("d:\\faceImgs");
    //图片格式
    private static String format = "jpg";
    //摄像头rtsp地址
    private static String rtsp = "rtsp://xxxxxxx";
    //人脸识别器位置
    //有：haarcascade_frontalface_alt.xml/haarcascade_frontalface_alt_tree.xml/haarcascade_frontalface_alt2.xml/haarcascade_frontalface_default.xml
    private static String altPath = "d:\\opencv\\sources\\data\\haarcascades\\haarcascade_frontalface_alt2.xml";

    static {
        //在调用之前，一定要加上这句话，目的是加载OpenCV API相关的DLL支持，没有它是不会正确运行的
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
    }

    /**
     * 将Mat对象转换为BufferedImage
     * @param mat
     * @return
     */
    private static BufferedImage mat2BI(Mat mat) {
        int dataSize = mat.cols() * mat.rows() * (int) mat.elemSize();
        byte[] data = new byte[dataSize];
        mat.get(0, 0, data);
        int type = mat.channels() == 1 ?
                BufferedImage.TYPE_BYTE_GRAY : BufferedImage.TYPE_3BYTE_BGR;

        if (type == BufferedImage.TYPE_3BYTE_BGR) {
            for (int i = 0; i < dataSize; i += 3) {
                byte blue = data[i + 0];
                data[i + 0] = data[i + 2];
                data[i + 2] = blue;
            }
        }
        BufferedImage image = new BufferedImage(mat.cols(), mat.rows(), type);
        image.getRaster().setDataElements(0, 0, mat.cols(), mat.rows(), data);
        return image;
    }

    /**
     * 将BufferedImage在窗口显示出来
     * @param g
     */
    public void paintComponent(Graphics g) {
        if (mImg != null) {
            g.drawImage(mImg, 0, 0, mImg.getWidth(), mImg.getHeight(), this);
        }
    }


    /**
     * opencv实现人脸识别、画框、转换为BufferedImage对象
     * @param img
     */
    public static BufferedImage detectFace(Mat img,CascadeClassifier faceDetector) {

        System.out.println("Running DetectFace ... ");

        //转为灰度图,直方图均衡化的前提
        Mat greyScaleImg = new Mat();
        Imgproc.cvtColor(img, greyScaleImg, Imgproc.COLOR_RGB2GRAY);

        //缩放图片,可提高检测速率,减少检测时间
        Mat smallImg=img.clone();
        Imgproc.resize(greyScaleImg,smallImg,new Size(img.width()*0.1,img.height()*0.1));

        //直方图均衡化,提高图像质量
        Mat qualityImg = new Mat();
        Imgproc.equalizeHist(smallImg,qualityImg);

        // 在图片中检测人脸
        MatOfRect faceDetections = new MatOfRect();
        faceDetector.detectMultiScale(qualityImg, faceDetections);

        //人脸坐标
        Rect[] rects = faceDetections.toArray();
        //如果检测到人脸
        if (rects != null && rects.length >= 1) {
            //画框
            for (Rect rect : rects) {
                Imgproc.rectangle(img, new Point(rect.x*10, rect.y*10),
                        new Point(rect.x*10 + rect.width*10, rect.y*10 + rect.height*10),
                        new Scalar(0, 255, 245), 2);
            }

            if (!path.exists()) {
                path.mkdirs();
            }
            String name = sdf.format(new Date());
            //将这一帧保存起来
            File f = new File(path + File.separator + name + "." + format);
            BufferedImage bufferedImage = mat2BI(img);
            try {
                ImageIO.write(bufferedImage, format, f);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return bufferedImage;
        } else {
            return mat2BI(img);
        }

        //测试是否正常接收摄像头视频流此方法只需这一步
        //return mat2BI(img);
    }


    public static void main(String[] args) {
        try {
            VideoCapture capture = new VideoCapture();  //打开摄像头,参数为0:代表本地摄像头
            //打开rtsp地址,需要把opencv\build\bin下的opencv_ffmpeg320_64.dll,拷贝一份到idea工作空间当前项目下,若是打开本地摄像头直接在上一步填入参数0即可，不需要capture.open(rtsp);这一步
            capture.open(rtsp);
            if (!capture.isOpened()) {
                //throw new Exception("camera not found!");
            }

            JFrame frame = new JFrame("camera");
            frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
            FaceCapture panel = new FaceCapture();
            frame.setContentPane(panel);
            //设置窗口尺寸
            /*frame.setSize((int) capture.get(Videoio.CAP_PROP_FRAME_WIDTH) + frame.getInsets().left + frame.getInsets().right,
                    (int) (capture.get(Videoio.CAP_PROP_FRAME_HEIGHT) + frame.getInsets().top + frame.getInsets().bottom));  全屏*/
            frame.setSize(1024,768);
            //打开窗口
            frame.setVisible(true);
            Mat capImg = new Mat();
            while (frame.isShowing()) {
                //读取一帧视频画面
                capture.read(capImg);

                //从配置文件lbpcascade_frontalface.xml中创建一个人脸识别器，该文件位于opencv安装目录中
                //opencv\\sources\\data\\haarcascades\\haarcascade_frontalface_alt.xml文件
                CascadeClassifier faceDetector = new CascadeClassifier(altPath);

                //识别人脸，转换为BufferedImage对象
                panel.mImg = detectFace(capImg,faceDetector);

                //刷新
                panel.repaint();
            }
            capture.release();
            //关闭Frame窗口
            frame.dispose();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("--done--");
        }
    }

}